package com.sharelords.spring.config;

import com.sharelords.biz.Biz;
import com.sharelords.biz.config.GeneralBizConfig;
import com.sharelords.biz.creator.ClassCreator;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;

/**
 * @Description: biz路径扫描
 * @Author: 千古龙少
 * @Time: 2019/12/7 20:39
 */
public class ClassPathBizScanner {

    private static final Logger logger = Logger.getLogger("ClassPathBizScanner");

    private static final PathMatchingResourcePatternResolver RESOLVER = new PathMatchingResourcePatternResolver();
    private static final SimpleMetadataReaderFactory REGISTER = new SimpleMetadataReaderFactory();
    private static final StandardEnvironment ENVIRONMENT = new StandardEnvironment();

    private GeneralBizConfig config;

    public ClassPathBizScanner(GeneralBizConfig config) {
        this.config = config;
    }

    /**
     * 初始方法
     *
     * @author 千古龙少
     * @date 2019年11月2日 下午5:24:53
     */
    public void initMethod() {
        logger.info("[General-biz]#######开始扫描相关Biz接口#######");
        long startTime = System.currentTimeMillis();
        // Biz接口的子接口获取
        Set<Class<?>> bizClassSet = this.getClazzFromInterface(Biz.class);

        // 创建class文件
        for (Class<?> clazz : bizClassSet) {
            ClassCreator.createClassFile(clazz);
        }

        logger.info("[General-biz]#######Biz接口实现类动态加载结束，耗时[" + (System.currentTimeMillis() - startTime) + "]毫秒");
    }

    /**
     * 获取继承指定Biz父接口的子接口
     *
     * @param clazz 指定class
     * @return Set<Class < ?>>
     * @author 千古龙少
     * @date 2019年9月8日 下午10:56:36
     */
    private Set<Class<?>> getClazzFromInterface(Class<?> clazz) {
        // 获取spring的包路径
        String pathPackage = getResourcePath();

        Set<Class<?>> paths = new HashSet<>();
        Resource[] resources;
        try {
            // 加载路径
            resources = RESOLVER.getResources(pathPackage);
        } catch (IOException e) {
            // 异常处理
            return Collections.emptySet();
        }
        for (Resource resource : resources) {
            MetadataReader metadataReader;
            try {
                // 读取资源
                metadataReader = REGISTER.getMetadataReader(resource);

                // 类信息
                ClassMetadata classMetadata = metadataReader.getClassMetadata();

                // 类全名
                String className = classMetadata.getClassName();

                // 加载类
                Class<?> destClazz = Class.forName(className);
                if (destClazz == clazz) {
                    continue;
                }
                // 是接口，并且有指定父类
                if (destClazz.isInterface() && clazz.isAssignableFrom(destClazz)) {
                    paths.add(destClazz);
                }
            } catch (Exception e) {
                logger.warning("资源读取失败" + e.toString());
            }
        }

        return paths;
    }

    /**
     * 根据包路径,获取Class的资源路径
     *
     * @return String
     * @author 千古龙少
     * @date 2019年8月31日 下午2:51:46
     */
    private String getResourcePath() {
        if (StringUtils.isEmpty(config.basePackages)) {
            return "";
        }

        return ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                + ClassUtils.convertClassNameToResourcePath(ENVIRONMENT.resolveRequiredPlaceholders(config.basePackages)) + '/'
                + "**/*.class";
    }

}
